using System;
using System.Collections.Generic;
using System.Text;

namespace GoogleCalendarReminder.gui
{
    /// <summary>
    /// A list box which displays a collection of GoogleCalendarReminder.EventEntryWrapper objects.
    /// </summary>
    public class EventListBox: System.Windows.Forms.ListBox
    {
        #region Private Data Members

        private System.Drawing.Font _itemTitleFont;
        private System.Drawing.Font _itemDescriptionFont;

        private System.Drawing.StringFormat _itemTitleStringFormat;
        private System.Drawing.StringFormat _itemDescriptionStringFormat;

        private int _itemTitlePadding = 4;
        private float _itemTitleFontSize = 16;

        private System.Drawing.Color _itemGradientTopColor = System.Drawing.SystemColors.ControlDark;
        private System.Drawing.Color _itemGradientBottomColor = System.Drawing.SystemColors.ControlLight;

        private EventEntryWrapperCollection _dataSource;

        //private 

        #endregion

        #region Private Functions

        /// <summary>
        /// Clears and adds items to the collection when the data source changes.
        /// </summary>
        private void DataSource_ListChanged(object sender, EventArgs e)
        {
            if (DataSource == null) return;

            if (InvokeRequired)
            {
                Invoke(new EventHandler(DataSource_ListChanged), sender, e);
            }
            else
            {
                EventEntryWrapper selectedItem = null;

                try
                {
                    selectedItem = SelectedItem;
                }
                catch (Exception) { }

                Items.Clear();
                Items.AddRange(DataSource.ToArray());

                try
                {
                    SelectedItem = selectedItem;
                }
                catch (Exception) { }

                RefreshItems();
            }
        }

        /// <summary>
        /// Generates the location/time string for an event.
        /// </summary>
        private string stringGenerator(EventEntryWrapper e)
        {
            StringBuilder sb = new StringBuilder();

            if (e.Location != string.Empty)
            {
                sb.Append("at ");
                sb.Append(e.Location);
                sb.Append(" ");
            }

            sb.Append("on ");
            sb.Append(e.Times[0].StartTime.ToShortDateString());
            sb.Append(" between ");
            sb.Append(e.Times[0].StartTime.ToShortTimeString());
            sb.Append(" and ");
            sb.Append(e.Times[0].EndTime.ToShortTimeString());
            sb.Append(".");

            return sb.ToString();
        }

        #endregion

        #region Protected Functions

        /// <summary>
        /// Refreshes the control whenever the size changes. This fixes drawing issues.
        /// </summary>
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            Refresh();
        }

        /// <summary>
        /// Draws the items in the list box.
        /// </summary>
        protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
        {
            base.OnDrawItem(e);

            if (DataSource == null || e.Index == -1) return;

            System.Drawing.Color topColorTmp = _itemGradientTopColor;

            System.Windows.Forms.DrawItemState state =
                e.State & System.Windows.Forms.DrawItemState.Selected;

            if (state > 0)
            {
                _itemGradientTopColor = System.Drawing.SystemColors.ActiveCaption;
            }

            System.Drawing.Drawing2D.LinearGradientBrush gBrush =
                new System.Drawing.Drawing2D.LinearGradientBrush(
                e.Bounds,
                ItemGradientTopColor,
                ItemGradientBottomColor,
                System.Drawing.Drawing2D.LinearGradientMode.Vertical);

            e.Graphics.FillRectangle(gBrush, e.Bounds);

            System.Drawing.RectangleF itemTitleRectangle =
                new System.Drawing.RectangleF(
                e.Bounds.X,
                e.Bounds.Y,
                e.Bounds.Width,
                _itemTitleFont.Height + _itemTitlePadding);

            System.Drawing.RectangleF itemDescriptionRectangle =
                new System.Drawing.RectangleF(
                e.Bounds.X,
                itemTitleRectangle.Bottom - _itemTitlePadding,
                e.Bounds.Width,
                _itemDescriptionFont.Height);

            string itemTitle = DataSource[e.Index].Title;

            if (itemTitle == string.Empty) itemTitle = "(No subject)";

            e.Graphics.DrawString(
                itemTitle,
                _itemTitleFont,
                System.Drawing.Brushes.Black,
                itemTitleRectangle,
                _itemTitleStringFormat);

            e.Graphics.DrawString(
                stringGenerator(DataSource[e.Index]),
                _itemDescriptionFont,
                System.Drawing.Brushes.Black,
                itemDescriptionRectangle,
                _itemDescriptionStringFormat);

            _itemGradientTopColor = topColorTmp;
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of the GoogleCalendarReminder.gui.EventListBox class.
        /// </summary>
        public EventListBox()
        {
            DoubleBuffered = true;
            base.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;

            _itemTitleFont =
                new System.Drawing.Font(
                Font.Name,
                _itemTitleFontSize,
                System.Drawing.FontStyle.Bold);

            _itemDescriptionFont =
                new System.Drawing.Font(
                Font.Name,
                Font.Size,
                System.Drawing.FontStyle.Regular);

            base.ItemHeight = _itemTitleFont.Height + _itemDescriptionFont.Height;

            _itemTitleStringFormat = new System.Drawing.StringFormat();
            _itemTitleStringFormat.Trimming = System.Drawing.StringTrimming.EllipsisCharacter;

            _itemDescriptionStringFormat = new System.Drawing.StringFormat();
            _itemDescriptionStringFormat.Trimming = System.Drawing.StringTrimming.EllipsisCharacter;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the data source of the control.
        /// </summary>
        [System.ComponentModel.DesignerSerializationVisibility(
            System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public new EventEntryWrapperCollection DataSource
        {
            get { return _dataSource; }
            set
            {
                Items.Clear();

                if (DataSource != null)
                {
                    DataSource.ListChanged -= new EventHandler(DataSource_ListChanged);
                }

                _dataSource = value;

                if (DataSource != null)
                {
                    DataSource.ListChanged += new EventHandler(DataSource_ListChanged);
                }

                DataSource_ListChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Gets the draw mode for the control.
        /// </summary>
        [System.ComponentModel.DesignerSerializationVisibility(
            System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public new System.Windows.Forms.DrawMode DrawMode
        {
            get { return base.DrawMode; }
        }

        /// <summary>
        /// Gets the item height.
        /// </summary>
        [System.ComponentModel.DesignerSerializationVisibility(
            System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public new int ItemHeight
        {
            get { return base.ItemHeight; }
        }

        /// <summary>
        /// Gets or sets the top gradient color.
        /// </summary>
        public System.Drawing.Color ItemGradientTopColor
        {
            get { return _itemGradientTopColor; }
            set
            {
                _itemGradientTopColor = value;
                Refresh();
            }
        }

        /// <summary>
        /// Gets or sets the bottom gradient color.
        /// </summary>
        public System.Drawing.Color ItemGradientBottomColor
        {
            get { return _itemGradientBottomColor; }
            set
            {
                _itemGradientBottomColor = value;
                Refresh();
            }
        }

        /// <summary>
        /// Gets or sets the selected item in the list box.
        /// </summary>
        [System.ComponentModel.DesignerSerializationVisibility(
            System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public new EventEntryWrapper SelectedItem
        {
            get
            {
                if (DataSource == null || SelectedIndex == -1) return null;
                return DataSource[SelectedIndex];
            }
            set
            {
                if (DataSource == null || value == null) return;

                for (int i = 0; i < DataSource.Count; i++)
                {
                    if (DataSource[i].Equals(value))
                    {
                        SelectedIndex = i;
                    }
                }
            }
        }

        #endregion
    }
}
